package db

import (
	"bytes"
	"errors"
	bolt "go.etcd.io/bbolt"
	"os"
	"sync"
	"time"
)

type EachCbk func(k, v []byte) error
type kvStrInt struct {
	key string
	val int
}

type kvStrStr struct {
	key string
	val string
}

var dbs sync.Map

func getDB(dbname string) (*bolt.DB, error) {
	path := "./dbs/"
	_ = os.Mkdir(path, 0755)
	//if path == "" {
	//	path = "./dbs/"
	//	_ = os.Mkdir(path, 0666)
	//}

	pathRuna := []rune(path)

	if string(pathRuna[len(pathRuna)-2:len(pathRuna)-1]) != "/" {
		path += "/"
	}

	if dbname != "" {
		db, ok := dbs.Load(dbname)
		if ok {
			return db.(*bolt.DB), nil
		} else {
			db, err := bolt.Open(path+dbname, 0666, &bolt.Options{Timeout: 1 * time.Second})
			if err != nil {
				//log.Fatal(err)
				return nil, err
			}
			dbs.Store(dbname, db)
			return db, nil
		}
	}
	//println("dbname can not empty")
	return nil, errors.New("dbname can not empty")
}

func Put(appid string, bucket string, key string, val string) error {
	db, err := getDB(appid)
	if db != nil {
		err := db.Update(func(tx *bolt.Tx) error {
			_, err := tx.CreateBucketIfNotExists([]byte(bucket))
			if err != nil {
				return err
			}
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				err = bBucket.Put([]byte(key), []byte(val))
				if err != nil {
					return err
				}
			}
			return nil
		})
		//log.Println(err)
		return err
	} else {
		return err
	}
}

func PutMapByte(appid string, bucket string, kv map[string][]byte) error {
	db, err := getDB(appid)
	if db != nil {
		err := db.Update(func(tx *bolt.Tx) error {
			_, err := tx.CreateBucketIfNotExists([]byte(bucket))
			if err != nil {
				return err
			}
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				for k, v := range kv {
					err = bBucket.Put([]byte(k), v)
					if err != nil {
						return err
					}
				}
			}
			return nil
		})
		return err
	} else {
		return err
	}
}

func PutMap(appid string, bucket string, kv map[string]string) error {
	kvByte := make(map[string][]byte)
	for k, v := range kv {
		kvByte[k] = []byte(v)
	}
	return PutMapByte(appid, bucket, kvByte)
}

//删除一个指定的key中的数据
func DeleteKey(appid string, bucket string, key string) error {
	db, err := getDB(appid)
	if db != nil {
		err := db.Update(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				err = bBucket.Delete([]byte(key))
				if err != nil {
					return err
				}
			}
			return nil
		})
		//log.Println(err)
		return err
	} else {
		return err
	}
}

//删除一个指定的key中的数据
func DeleteKeySlice(appid string, bucket string, keys []string) error {
	db, err := getDB(appid)
	if db != nil {
		err := db.Update(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				for e := range keys {
					err = bBucket.Delete([]byte(keys[e]))
					if err != nil {
						return err
					}
				}
			}
			return nil
		})
		//log.Println(err)
		return err
	} else {
		return err
	}
}

// DeleteMap 从bolt中删除一个指定的Map中的所有key
func DeleteMap(appid string, bucket string, keysMap map[string]interface{}) error {
	db, err := getDB(appid)
	if db != nil {
		err := db.Update(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				for k, _ := range keysMap {
					err = bBucket.Delete([]byte(k))
					if err != nil {
						return err
					}
				}
			}
			return nil
		})
		//log.Println(err)
		return err
	} else {
		return err
	}
}

// DeleteMap 从bolt中删除一个指定的Bucket
func DeleteBucket(appid string, bucket string) error {
	db, err := getDB(appid)
	if db != nil {
		err := db.Update(func(tx *bolt.Tx) error {
			err = tx.DeleteBucket([]byte(bucket))
			return err
		})
		return err
	} else {
		return err
	}
}

//只读获取
func Get(appid string, bucket string, key string) string {
	db, _ := getDB(appid)
	val := ""
	if db != nil {
		_ = db.View(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				val = string(bBucket.Get([]byte(key)))
			}
			return nil
		})
	}
	return val
}

//获取带前缀的
func GetMap(appid string, bucket string, prefix string) map[string]string {
	db, _ := getDB(appid)
	val := make(map[string]string)
	if db != nil {
		_ = db.View(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				if prefix == "" {
					_ = bBucket.ForEach(func(k, v []byte) error {
						//fmt.Printf("key=%s, value=%s\n", k, v)
						kStr := string(k)
						val[kStr] = string(v)
						return nil
					})
				} else {
					c := bBucket.Cursor()
					if c != nil {
						_prefix := []byte(prefix)
						for k, v := c.Seek(_prefix); k != nil && bytes.HasPrefix(k, _prefix); k, v = c.Next() {
							//fmt.Printf("key=%s, value=%s\n", k, v)
							kStr := string(k)
							val[kStr] = string(v)
						}
					}
				}
			}
			return nil
		})
	}
	return val
}

//获取带前缀的数据，返回切片
func GetSlice(appid string, bucket string, prefix string) []kvStrStr {
	db, _ := getDB(appid)
	var val []kvStrStr
	if db != nil {
		_ = db.View(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				if prefix == "" {
					_ = bBucket.ForEach(func(k, v []byte) error {
						//fmt.Printf("key=%s, value=%s\n", k, v)
						kStr := string(k)
						val = append(val, kvStrStr{key: kStr, val: string(v)})
						//val[kStr] = string(v)
						return nil
					})
				} else {
					c := bBucket.Cursor()
					if c != nil {
						_prefix := []byte(prefix)
						for k, v := c.Seek(_prefix); k != nil && bytes.HasPrefix(k, _prefix); k, v = c.Next() {
							//fmt.Printf("key=%s, value=%s\n", k, v)
							kStr := string(k)
							val = append(val, kvStrStr{key: kStr, val: string(v)})
						}
					}
				}
			}
			return nil
		})
	}
	return val
}

//遍历获取
func GetEach(appid string, bucket string, eachCbk EachCbk) map[string]string {
	db, _ := getDB(appid)
	val := make(map[string]string)
	if db != nil {
		_ = db.View(func(tx *bolt.Tx) error {
			bBucket := tx.Bucket([]byte(bucket))
			if bBucket != nil {
				_ = bBucket.ForEach(eachCbk)
			}
			return nil
		})
	}
	return val
}
